/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package sputty.classes;

import ch.ethz.ssh2.Connection;
import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.swing.ImageIcon;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTabbedPane;
import javax.swing.JTextPane;
import javax.swing.UIManager;

/**
 *
 * @author mauro
 */
public class TerminalAreaExtended extends JTextPane implements KeyListener, Runnable{

    private InputStream in;
    private OutputStream out;
    private JPopupMenu mnu;
    private Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();
    private Connection C;
    
    //VARIABILI DA JCTerm
    
    private BufferedImage img;
    private BufferedImage background;
    private Graphics2D cursor_graphics;
    private Graphics2D graphics;
    private java.awt.Color defaultbground=Color.black;
    private java.awt.Color defaultfground=Color.white;
    private java.awt.Color bground=Color.black;
    private java.awt.Color fground=Color.white;
    private java.awt.Component term_area=null;
    private java.awt.Font font;

    private boolean bold=false;
    private boolean underline=false;
    private boolean reverse=false;

    private int term_width=80;
    private int term_height=24;

    private int descent=0;

    private int x=0;
    private int y=0;

    private int char_width;
    private int char_height;
    
    private int region_y2;
    private int region_y1;

    protected int tab=8;

    //private int line_space=0;
    private int line_space=-2;
    private int compression=0;

    private boolean antialiasing=true;

    private final Object[] colors= {Color.black, Color.red, Color.green, Color.yellow, Color.blue, Color.magenta, Color.cyan, Color.white};

    byte[] obuffer=new byte[3];
    
    byte[] buf=new byte[1024];
    int bufs=0;
    int buflen=0;
    
    private byte[] b2=new byte[2];
    private byte[] b1=new byte[1];
    
    //FINE VARIABILI da JCTerm
    
    public TerminalAreaExtended(){
        init();
    }
    
    public void run(){

        reset();

        int[] intarg=new int[10];
        int intargi=0;

        x=0;
        y=char_height;

        byte b;

        try{
            while(true){
                b=getChar();
                if(b==0)continue;
                if(b==0x1b){
                    b = getChar();
                    if(b=='M'){ // sr \EM sr scroll text down (P)
                        scroll_reverse();
                        continue;
                    }

                    if(b=='D'){ // sf
                        scroll_forward();
                        continue;
                    }

                    if(b=='7'){
                        save_cursor();
                        continue;
                    }
                    
                    if(b=='('){
                        b=getChar();
                        if(b=='B'){
                            b=getChar();
                            if(b==0x1b){
                                b=getChar();
                                if(b==')'){
                                    b=getChar();
                                    if(b=='0'){ // enacs
                                        ena_acs();
                                        continue;
                                    }else{
                                        pushChar((byte)'0');
                                    }
                                }else{
                                    pushChar((byte)')');
                                }
                            }else{
                                pushChar((byte)0x1b);
                            }
                        }else{
                            pushChar((byte)'B');
                        }
                    }
                    
                    if(b=='>'){
                        b=getChar(); // 0x1b
                        b=getChar(); // '['
                        b=getChar(); // '?'
                        b=getChar(); // '3'
                        b=getChar(); // 'l'
                        b=getChar(); // 0x1b
                        b=getChar(); // '['
                        b=getChar(); // '?'
                        b=getChar(); // '4'
                        b=getChar(); // 'l'
                        b=getChar(); // 0x1b
                        b=getChar(); // '['
                        b=getChar(); // '?'
                        b=getChar(); // '5'
                        b=getChar(); // 'l'
                        b=getChar(); // 0x1b
                        b=getChar(); // '['
                        b=getChar(); // '?'
                        b=getChar(); // '7'
                        b=getChar(); // 'h'
                        b=getChar(); // 0x1b
                        b=getChar(); // '['
                        b=getChar(); // '?'
                        b=getChar(); // '8'
                        b=getChar(); // 'h'

                        reset_2string();
                        continue;
                    }
                    
                    if(b!='['){
                        pushChar(b);
                        continue;
                    }
                    
                    intargi=0;
                    intarg[intargi]=0;
                    int digit=0;

                    while(true){
                        b=getChar();
                        //System.out.print("#"+new Character((char)b)+"["+Integer.toHexString(b&0xff)+"]");
                        if(b==';'){
                            if(digit>0){
                                intargi++;
                                intarg[intargi]=0;
                                digit=0;
                            }
                            continue;
                        }

                        if('0'<=b&&b<='9'){
                            intarg[intargi]=intarg[intargi]*10+(b-'0');
                            digit++;
                            continue;
                        }

                        pushChar(b);
                        break;
                    }
                    
                    b=getChar();


                    if(b=='m'){

                        if(digit==0&&intargi==0){
                            b=getChar();
                            if(b==0x0f){ // sgr0
                                exit_attribute_mode();
                                continue;
                            }else{ // rmso, rmul
                                exit_underline_mode();
                                exit_standout_mode();
                                pushChar(b);
                                continue;
                            }
                        }

                        for(int i=0; i<=intargi; i++){
                            Object fg=null;
                            Object bg=null;
                            Object tmp=null;

                            switch(intarg[i]){
                                case 0: // Reset all attributes
                                    exit_standout_mode();
                                    continue;
                                case 1: // Bright  // bold
                                    enter_bold_mode();
                                    continue;
                                case 2: // Dim
                                    break;
                                case 4: // Underline
                                    enter_underline_mode();
                                    continue;
                                case 5: // Blink
                                case 8: // Hidden
                                    break;
                                case 7: // reverse
                                    enter_reverse_mode();
                                    continue;
                                case 30:
                                case 31:
                                case 32:
                                case 33:
                                case 34:
                                case 35:
                                case 36:
                                case 37:
                                    tmp=this.getColor(intarg[i]-30);
                                    if(tmp!=null)fg=tmp;
                                    break;
                                case 40:
                                case 41:
                                case 42:
                                case 43:
                                case 44:
                                case 45:
                                case 46:
                                case 47:
                                    tmp=this.getColor(intarg[i]-40);
                                    if(tmp!=null)bg=tmp;
                                    break;
                                default:
                                    break;
                            }
                            if(fg!=null)this.setForeGround(fg);
                            if(bg!=null)this.setBackGround(bg);
                        }
                        //System.out.println("fg: "+fg+" bg: "+bg);
                        continue;
                    }
                    
                    if(b=='r'){ // csr
                        change_scroll_region(intarg[0], intarg[1]);
                        //System.out.println("r: "+region_y1+", "+region_y2+", intargi="+intargi);
                        continue;
                    }
                    
                    if(b=='H'){ // cup
                        /*
                        b=getChar();
                        if(b!='$'){      // home
                          pushChar(b);
                        }
                        else{
                          b=getChar();  // <
                          b=getChar();  // 5
                          b=getChar();  // >
                        }
                        */

                        if(digit==0&&intargi==0){
                            intarg[0]=intarg[1]=1;
                        }

                        //System.out.println("H: "+region_y1+", "+region_y2+", intargi="+intargi);
                        cursor_address(intarg[0], intarg[1]);
                        continue;
                    }

                    if(b=='B'){ // cud
                        parm_down_cursor(intarg[0]);
                        continue;
                    }

                    if(b=='D'){ // cub
                        parm_left_cursor(intarg[0]);
                        continue;
                    }

                    if(b=='C'){ // cuf
                        if(digit==0&&intargi==0){
                            intarg[0]=1;
                        }
                        parm_right_cursor(intarg[0]);
                        continue;
                    }

                    if(b=='K'){ // el
                    /*
                          b=getChar(); // 
                          if(b=='$'){
                            b=getChar(); // < 
                            b=getChar(); // 3 
                            b=getChar(); // > 
                          }
                          else{
                            pushChar(b);
                          }
                    */

                        if(digit==0&&intargi==0){ // el
                            clr_eol();
                        }else{ // el1
                            clr_bol();
                        }
                        continue;
                    }
                    
                    if(b=='J'){
                        //for(int i=0; i<intargi; i++){ System.out.print(intarg[i]+" ");}
                        //System.out.println(intarg[0]+"<- intargi="+intargi);
                        clr_eos();
                        continue;
                    }

                    if(b=='A'){ // cuu
                        if(digit==0&&intargi==0){
                            intarg[0]=1;
                        }
                        parm_up_cursor(intarg[0]);
                        continue;
                    }
                    
                    if(b=='?'){
                        b=getChar();
                        if(b=='1'){
                            b=getChar();
                            if(b=='l'||b=='h'){
                                b=getChar();
                                if(b==0x1b){
                                    b=getChar();
                                    if(b=='>'|| // rmkx   , leave 'keybroad_transmit' mode
                                        b=='='){ // smkx   , enter 'keyborad_transmit' mode 
                                        // TODO
                                        continue;
                                    }
                                }
                            }else if(b=='h'){
                                b=getChar();
                                if(b==0x1b){
                                    b=getChar();
                                    if(b=='='){ // smkx enter 'keyborad_transmit' mode
                                        continue;
                                    }
                                }
                            }
                        }else if(b=='7'){
                            b=getChar();
                            if(b=='h'){ // smam
                                // TODO
                                //System.out.println("turn on automatic magins");
                                continue;
                            }else if(b=='l'){ // rmam
                                // TODO
                                //System.out.println("turn off automatic magins");
                                continue;
                            }
                            pushChar(b);
                            b='7';
                        }else{
                        }
                    }
                    
                    if(b=='h'){ // kh \Eh home key
                        continue;
                    }
                    continue;
                }
                
                if(b==0x07){ // bel ^G
                    bell();
                    continue;
                }

                if(b==0x09){ // ht(^I)
                    tab();
                    continue;
                }

                if(b==0x0f){ // rmacs ^O  	// end alternate character set (P)
                    exit_alt_charset_mode();
                    continue;
                }

                if(b==0x0e){ // smacs ^N  	// start alternate character set (P)
                    enter_alt_charset_mode();
                    continue;
                }

                if(b==0x0d){
                    carriage_return();
                    continue;
                }

                if(b==0x08){
                    cursor_left();
                    continue;
                }

                if(b==0x0a){ // '\n'
                    //System.out.println("x="+x+",y="+y);
                    cursor_down();
                    //check_region();
                    continue;
                }

                if(b!=0x0a){ // !'\n'
                    pushChar(b);
                    draw_text();
                    continue;
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    
    private void init(){
        
        enableEvents(AWTEvent.KEY_EVENT_MASK);
        addKeyListener(this);

        setFont("Monospaced-12");

        background=new BufferedImage(char_width, char_height, BufferedImage.TYPE_INT_RGB);
        {
          Graphics2D foog=(Graphics2D)(background.getGraphics());
          foog.setColor(getBackGround());
          foog.fillRect(0, 0, char_width, char_height);
          foog.dispose();
        }

        setSize(getTermWidth(), getTermHeight());

        clear();

        term_area=this;

        setPreferredSize(new Dimension(getTermWidth(), getTermHeight()));

        setSize(getTermWidth(), getTermHeight());
        setFocusable(true);
        enableInputMethods(true);

        setFocusTraversalKeysEnabled(false);
        //  setOpaque(true);
        
        mnu = new JPopupMenu();
        mnu.setBorderPainted(true);
        mnu.setInvoker(this);
        mnu.setFont(new Font(Font.DIALOG, Font.BOLD, 10));
        JMenuItem mnuCopy = new JMenuItem("Copia", new ImageIcon(this.getClass().getResource("/sputty/gui/resources/popupmenus/editcopy.png")));
        mnuCopy.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                mnuCopyActionListener(e);
            }
        });
        mnuCopy.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseEntered(MouseEvent evt){
                mnuHighlightON((JMenuItem)evt.getSource());
            }
            @Override
            public void mouseExited(MouseEvent evt){
                mnuHighlightOFF((JMenuItem)evt.getSource());
            }
        });
        mnuCopy.setFont(new Font(Font.DIALOG, Font.BOLD, 10));
        mnu.add(mnuCopy);
        JMenuItem mnuPaste = new JMenuItem("Incolla", new ImageIcon(this.getClass().getResource("/sputty/gui/resources/popupmenus/editpaste.png")));
        mnuPaste.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                mnuPasteActionListener(e);
            }
        });
        mnuPaste.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseEntered(MouseEvent evt){
                mnuHighlightON((JMenuItem)evt.getSource());
            }
            @Override
            public void mouseExited(MouseEvent evt){
                mnuHighlightOFF((JMenuItem)evt.getSource());
            }
        });
        mnuPaste.setFont(new Font(Font.DIALOG, Font.BOLD, 10));
        mnu.add(mnuPaste);
        JMenuItem mnuCopyNPaste = new JMenuItem("Copia & Incolla", new ImageIcon(this.getClass().getResource("/sputty/gui/resources/popupmenus/cpynpaste.png")));
        mnuCopyNPaste.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                mnuCopyNPasteActionListener(e);
            }
        });
        mnuCopyNPaste.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseEntered(MouseEvent evt){
                mnuHighlightON((JMenuItem)evt.getSource());
            }
            @Override
            public void mouseExited(MouseEvent evt){
                mnuHighlightOFF((JMenuItem)evt.getSource());
            }
        });
        mnuCopyNPaste.setFont(new Font(Font.DIALOG, Font.BOLD, 10));
        mnu.add(mnuCopyNPaste);
        
        mnu.addSeparator();
        
        JMenuItem mnuDisconnect = new JMenuItem("Disconnetti", new ImageIcon(this.getClass().getResource("/sputty/gui/resources/popupmenus/logout.png")));
        mnuDisconnect.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                mnuDisconnectActionListener(e);
            }
        });
        mnuDisconnect.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseEntered(MouseEvent evt){
                mnuHighlightON((JMenuItem)evt.getSource());
            }
            @Override
            public void mouseExited(MouseEvent evt){
                mnuHighlightOFF((JMenuItem)evt.getSource());
            }
        });
        mnuDisconnect.setFont(new Font(Font.DIALOG, Font.BOLD, 10));
        mnu.add(mnuDisconnect);
        
        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                TerminalAreaExtendedMouseClicked(e);
            }
        });
    }
    
    public void setInputStream(InputStream in){
        this.in = in;
    }
    
    public InputStream getInputStram(){
        return this.in;
    }
    
    public void setOutputStream(OutputStream out){
        this.out = out;
    }
    
    public OutputStream getOutputStream(){
        return this.out;
    }
    
    public void setConnection(Connection C){
        this.C = C;
    }
    
    public Connection getConnection(){
        return this.C;
    }
    
    private void TerminalAreaExtendedMouseClicked(MouseEvent e){
        if(e.getButton() == e.BUTTON3){
            mnu.setLocation(e.getXOnScreen(), e.getYOnScreen());
            mnu.setVisible(true);
            mnu.grabFocus();
        }
    }
    
    private void mnuCopyActionListener(ActionEvent e){
        if(this.getSelectionStart() > 0){
            StringSelection s = new StringSelection(this.getSelectedText());
            clip.setContents(s, null);
            mnu.setVisible(false);
            this.grabFocus();
        }
    }
    
    private void mnuPasteActionListener(ActionEvent e){
        if(clip.isDataFlavorAvailable(DataFlavor.stringFlavor)){
            try{
                Transferable T = clip.getContents(null);
                String[] data = new String[1];
                data[0] = (String)T.getTransferData(DataFlavor.stringFlavor);
                new SendCommands(in, out, data, 0,false).run();
                mnu.setVisible(false);
                this.grabFocus();
            }catch(Exception ex){
                ex.printStackTrace();
            }
        }
    }
    
    private void mnuCopyNPasteActionListener(ActionEvent e){
        if(this.getSelectionStart() > 0){
            StringSelection s = new StringSelection(this.getSelectedText());
            clip.setContents(s, null);
            try{
                Transferable T = clip.getContents(null);
                String[] data = new String[1];
                data[0] = (String)T.getTransferData(DataFlavor.stringFlavor);
                new SendCommands(in, out, data, 0,false).run();
                mnu.setVisible(false);
            }catch(Exception ex){
                ex.printStackTrace();
            }
            mnu.setVisible(false);
            this.grabFocus();
        }        
    }
    private void mnuHighlightON(JMenuItem mnuItem){
        mnuItem.setBackground(UIManager.getColor("MenuItem.selectionBackground"));  
        mnuItem.setForeground(UIManager.getColor("MenuItem.selectionForeground"));
    }
    
    private void mnuHighlightOFF(JMenuItem mnuItem){
        mnuItem.setBackground(UIManager.getColor("MenuItem.background"));   
        mnuItem.setForeground(UIManager.getColor("MenuItem.foreground"));
    }
    
    private void mnuDisconnectActionListener(ActionEvent e){
        C.close();        
        ((JTabbedPane)(this.getParent().getParent().getParent().getParent())).remove(((JTabbedPane)(this.getParent().getParent().getParent().getParent())).getSelectedIndex());
        mnu.setVisible(false);
    }
    
    public void processKeyEvent(KeyEvent e){
        //System.out.println(e);
        int id=e.getID();
        if(id==KeyEvent.KEY_PRESSED){
          keyPressed(e);
        }else if(id==KeyEvent.KEY_RELEASED){ 
          keyReleased(e);
        }else if(id==KeyEvent.KEY_TYPED){
            keyTyped(e);
        }
        e.consume(); // ??
    }
    
    public void keyReleased(KeyEvent e){
        
    }

    public void keyPressed(KeyEvent e){
        int keycode=e.getKeyCode();
        byte[] code=null;
        switch(keycode){
          case KeyEvent.VK_CONTROL:
          case KeyEvent.VK_SHIFT:
          case KeyEvent.VK_ALT:
          case KeyEvent.VK_CAPS_LOCK:
            return;
          case KeyEvent.VK_ENTER:
            //code=emulator.getCodeENTER();
            break;
          case KeyEvent.VK_UP:
            //code=emulator.getCodeUP();
            break;
          case KeyEvent.VK_DOWN:
            //code=emulator.getCodeDOWN();
            break;
          case KeyEvent.VK_RIGHT:
            //code=emulator.getCodeRIGHT();
            break;
          case KeyEvent.VK_LEFT:
            //code=emulator.getCodeLEFT();
            break;
          case KeyEvent.VK_F1:
            //code=emulator.getCodeF1();
            break;
          case KeyEvent.VK_F2:
            ///code=emulator.getCodeF2();
            break;
          case KeyEvent.VK_F3:
            //code=emulator.getCodeF3();
            break;
          case KeyEvent.VK_F4:
            //code=emulator.getCodeF4();
            break;
          case KeyEvent.VK_F5:
            //code=emulator.getCodeF5();
            break;
          case KeyEvent.VK_F6:
            //code=emulator.getCodeF6();
            break;
          case KeyEvent.VK_F7:
            //code=emulator.getCodeF7();
            break;
          case KeyEvent.VK_F8:
            //code=emulator.getCodeF8();
            break;
          case KeyEvent.VK_F9:
            //code=emulator.getCodeF9();
            break;
          case KeyEvent.VK_F10:
            //code=emulator.getCodeF10();
            break;
          case KeyEvent.VK_TAB:
            //code=emulator.getCodeTAB();
            break;
        }
        if(code!=null){
          try{
            out.write(code, 0, code.length);
            out.flush();
          }catch(Exception ee){
          }
          return;
        }

        char keychar=e.getKeyChar();
        if((keychar&0xff00)==0){
            obuffer[0]=(byte)(e.getKeyChar());
            try{
                out.write(obuffer, 0, 1);
                out.flush();
            }catch(Exception ee){
            }
        }
    }

    public void keyTyped(KeyEvent e){
        char keychar=e.getKeyChar();
        if((keychar&0xff00)!=0){
            char[] foo=new char[1];
            foo[0]=keychar;
            try{
                byte[] goo=new String(foo).getBytes("EUC-JP");
                out.write(goo, 0, goo.length);
                out.flush();
            }catch(Exception eee){
            }
        }
    }
    
    private void setFont(String fname){
        font=java.awt.Font.decode(fname);
        BufferedImage img=new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics=(Graphics2D)(img.getGraphics());
        graphics.setFont(font);
        {
            FontMetrics fo=graphics.getFontMetrics();
            descent=fo.getDescent();
            /*
            System.out.println(fo.getDescent());
            System.out.println(fo.getAscent());
            System.out.println(fo.getLeading());
            System.out.println(fo.getHeight());
            System.out.println(fo.getMaxAscent());
            System.out.println(fo.getMaxDescent());
            System.out.println(fo.getMaxDecent());
            System.out.println(fo.getMaxAdvance());
            */
            char_width=(int)(fo.charWidth((char)'@'));
            char_height=(int)(fo.getHeight())+(line_space*2);
            descent+=line_space;
        }
        img.flush();
        graphics.dispose();
    }
    
    private java.awt.Color getBackGround(){
        if(reverse)return fground;
        return bground;
    }

    private java.awt.Color getForeGround(){
        if(reverse)return bground;
        return fground;
    }
    
    public int getTermWidth(){
        return char_width*term_width;
    }

    public int getTermHeight(){
        return char_height*term_height;
    }
    
    public void clear(){
        graphics.setColor(getBackGround());
        graphics.fillRect(0, 0, char_width*term_width, char_height*term_height);
        graphics.setColor(getForeGround());
    }
    
    public void setCursor(int x, int y){
        this.x=x;
        this.y=y;
    }

    public void draw_cursor(){
        cursor_graphics.fillRect(x, y-char_height, char_width, char_height);
        repaint(x, y-char_height, char_width, char_height);
    }

    public void redraw(int x, int y, int width, int height){
        repaint(x, y, width, height);
    }

    public void clear_area(int x1, int y1, int x2, int y2){
    //System.out.println("clear_area: "+x1+" "+y1+" "+x2+" "+y2);
        graphics.setColor(getBackGround());
        graphics.fillRect(x1, y1, x2-x1, y2-y1);
        graphics.setColor(getForeGround());
    }

    public void scroll_area(int x, int y, int w, int h, int dx, int dy){
    //System.out.println("scroll_area: "+x+" "+y+" "+w+" "+h+" "+dx+" "+dy);
        graphics.copyArea(x, y, w, h, dx, dy);
        repaint(x+dx, y+dy, w, h);
    }

    public void drawBytes(byte[] buf, int s, int len, int x, int y){
        //    clear_area(x, y, x+len*char_width, y+char_height);
        //    graphics.setColor(getForeGround());

        //System.out.println("drawString: "+x+","+y+" "+len+" "+new String(buf, s, len));

        graphics.drawBytes(buf, s, len, x, y-descent);
        if(bold)graphics.drawBytes(buf, s, len, x+1, y-descent);
        if(underline){
            graphics.drawLine(x, y-1, x+len*char_width, y-1);
        }
    }

    public void drawString(String str, int x, int y){
    //    clear_area(x, y, x+str.length()*char_width, y+char_height);
    //    graphics.setColor(getForeGround());
        graphics.drawString(str, x, y-descent);
        if(bold)graphics.drawString(str, x+1, y-descent);
        if(underline){
            graphics.drawLine(x, y-1, x+str.length()*char_width, y-1);
        }
    }
    
    public int getColumnCount(){
        return term_width;
    }
    
    public int getRowCount(){
        return term_height;
    }
    
    public int getCharWidth(){
        return char_width;
    }

    public int getCharHeight(){
        return char_height;
    }
    
    byte getChar() throws java.io.IOException{
        if(buflen==0){
            fillBuf();
        }
        buflen--;
        return buf[bufs++];
    }
    
    void fillBuf() throws java.io.IOException{
        buflen=bufs=0;
        buflen=in.read(buf, bufs, buf.length-bufs);
        if(buflen<=0){
            buflen=0;
            throw new IOException("fillBuf");
        }
    }
    
    void pushChar(byte foo) throws java.io.IOException{
        //System.out.println("pushChar: "+new Character((char)foo)+"["+Integer.toHexString(foo&0xff)+"]");
        buflen++;
        buf[--bufs]=foo;
    }

    int getASCII(int len) throws java.io.IOException{
        //System.out.println("bufs="+bufs+", buflen="+buflen+", len="+len);
        if(buflen==0){
            fillBuf();
        }
        if(len>buflen)
            len=buflen;
            int foo=len;
            byte tmp;
            while(len>0){
                tmp=buf[bufs++];
                if(0x20<=tmp&&tmp<=0x7f){
                    buflen--;
                    len--;
                    continue;
                }
                bufs--;
                break;
            }
            //System.out.println(" return "+(foo-len));
            return foo-len;
    }
    
    public void reset(){
        term_width =this.getColumnCount();
        term_height = this.getRowCount();
        char_width = this.getCharWidth();
        char_height = this.getCharHeight();
        region_y1 = 1;
        region_y2 = term_height;
    }
    
    protected void scroll_reverse(){
        this.draw_cursor();
        this.scroll_area(0, (region_y1-1)*char_height, term_width*char_width, (region_y2-region_y1)*char_height, 0, char_height);
        this.clear_area(x, y-char_height, term_width*char_width, y);
        this.redraw(0, 0, term_width*char_width, term_height*char_height -char_height);
        //term.setCursor(x, y);
        this.draw_cursor();
    }

    // Normal scroll one line
    protected void scroll_forward(){
        this.draw_cursor();
        this.scroll_area(0, (region_y1-1)*char_height, term_width*char_width, (region_y2-region_y1+1)*char_height, 0, -char_height);
        this.clear_area(0, region_y2*char_height-char_height, term_width*char_width, region_y2*char_height);
        this.redraw(0, (region_y1-1)*char_height, term_width*char_width, (region_y2 -region_y1+1) *char_height);
        this.draw_cursor();
    }

    // Save cursor position
    protected void save_cursor(){
        // TODO
        //System.out.println("save current cursor position");
    }
    
     // Enable alternate character set
    protected void ena_acs(){
        // TODO
        //System.out.println("enable alterate char set");
    }

    protected void exit_alt_charset_mode(){
        // TODO
        //System.out.println("end alternate character set (P)");
    }

    protected void enter_alt_charset_mode(){
        // TODO
        //System.out.println("start alternate character set (P)");
    }

    protected void reset_2string(){
        // TODO
        // rs2(reset string)
    }

    protected void exit_attribute_mode(){
        // TODO
        //System.out.println("turn off all attributes");
        this.resetAllAttributes();
    }

    protected void exit_standout_mode(){
        this.resetAllAttributes();
    }

    protected void exit_underline_mode(){
        // TODO
    }

    protected void enter_bold_mode(){
        this.setBold();
    }

    protected void enter_underline_mode(){
        this.setUnderline();
    }

    protected void enter_reverse_mode(){
        this.setReverse();
    }

    protected void change_scroll_region(int y1, int y2){
        region_y1=y1;
        region_y2=y2;
    }

    protected void cursor_address(int r, int c){
        this.draw_cursor();
        x=(c-1)*char_width;
        y=r*char_height;
        //System.out.println("setCourosr: "+x+" "+y);
        this.setCursor(x, y);
        this.draw_cursor();
    }

    protected void parm_down_cursor(int lines){
        this.draw_cursor();
        y+=(lines)*char_height;
        this.setCursor(x, y);
        this.draw_cursor();
    }

    protected void parm_left_cursor(int chars){
        this.draw_cursor();
        x-=(chars)*char_width;
        this.setCursor(x, y);
        this.draw_cursor();
    }

    protected void parm_right_cursor(int chars){
        this.draw_cursor();
        x+=(chars)*char_width;
        this.setCursor(x, y);
        this.draw_cursor();
    }

    protected void clr_eol(){
        this.draw_cursor();
        this.clear_area(x, y-char_height, term_width*char_width, y);
        this.redraw(x, y-char_height, (term_width)*char_width-x, char_height);
        this.draw_cursor();
    }

    protected void clr_bol(){
        this.draw_cursor();
        this.clear_area(0, y-char_height, x, y);
        this.redraw(0, y-char_height, x, char_height);
        this.draw_cursor();
    }

    protected void clr_eos(){
        this.draw_cursor();
        this.clear_area(x, y-char_height, term_width*char_width, term_height *char_height);
        this.redraw(x, y-char_height, term_width*char_width-x, term_height *char_height-y+char_height);
        this.draw_cursor();
    }

    protected void parm_up_cursor(int lines){
        this.draw_cursor();
        //	  x=0;
        //	  y-=char_height;
        y-=(lines)*char_height;
        this.setCursor(x, y);
        this.draw_cursor();
    }

    protected void bell(){
        //this.beep();
    }

    protected void tab(){
        this.draw_cursor();
        x=(((x/char_width)/tab+1)*tab*char_width);
        if(x>=term_width*char_width){
            x=0;
            y+=char_height;
        }
        this.setCursor(x, y);
        this.draw_cursor();
    }

    protected void carriage_return(){
        this.draw_cursor();
        x=0;
        this.setCursor(x, y);
        this.draw_cursor();
    }

    protected void cursor_left(){
        this.draw_cursor();
        x-=char_width;
        if(x<0){
            y-=char_height;
            x=term_width*char_width-char_width;
        }
        this.setCursor(x, y);
        this.draw_cursor();
    }

    protected void cursor_down(){
        this.draw_cursor();
        y+=char_height;
        this.setCursor(x, y);
        this.draw_cursor();
        check_region();
    }
    private void check_region(){
        if(x>=term_width*char_width){
            //System.out.println("!! "+new Character((char)b)+"["+Integer.toHexString(b&0xff)+"]");
            x=0;
            y+=char_height;
            //System.out.println("@1: ry="+ry);
        }

        if(y>region_y2*char_height){
            while(y>region_y2*char_height){
                y-=char_height;
            }
            this.draw_cursor();
            this.scroll_area(0, region_y1*char_height, term_width*char_width, (region_y2-region_y1)*char_height, 0, -char_height);
            this.clear_area(0, y-char_height, term_width*char_width, y);
            this.redraw(0, 0, term_width*char_width, region_y2*char_height);
            this.setCursor(x, y);
            this.draw_cursor();
        }
    }
    
    public void resetAllAttributes(){
        bold=false;
        underline=false;
        reverse=false;
        bground=defaultbground;
        fground=defaultfground;
        if(graphics!=null)graphics.setColor(getForeGround());
    }
    
    public void setBold(){
        bold=true;
    }

    public void setUnderline(){
        underline=true;
    }

    public void setReverse(){
        reverse=true;
        if(graphics!=null)graphics.setColor(getForeGround());
    }
    
    public Object getColor(int index){
        if(colors==null||index<0||colors.length<=index)return null;
        return colors[index];
    }
    
    public void setForeGround(Object f){
        fground=toColor(f);
        graphics.setColor(getForeGround());
    }

    public void setBackGround(Object b){
        bground=toColor(b);
        Graphics2D foog=(Graphics2D)(background.getGraphics());
        foog.setColor(getBackGround());
        foog.fillRect(0, 0, char_width, char_height);
        foog.dispose();
    }
    
    private java.awt.Color toColor(Object o){
        if(o instanceof String){
            return java.awt.Color.getColor((String)o);
        }
        if(o instanceof java.awt.Color){
            return (java.awt.Color)o;
        }
        return Color.white;
    }
    
    protected void draw_text() throws java.io.IOException{
        int rx;
        int ry;
        int w;
        int h;

        check_region();

        rx=x;
        ry=y;

        byte b=getChar();
        this.draw_cursor();
        //System.out.print(new Character((char)b)+"["+Integer.toHexString(b&0xff)+"]");
        if((b&0x80)!=0){
          this.clear_area(x, y-char_height, x+char_width*2, y);
          b2[0]=b;
          b2[1]=getChar();
          this.drawString(new String(b2, 0, 2, "EUC-JP"), x, y);
          x+=char_width;
          x+=char_width;
          w=char_width*2;
          h=char_height;
        }
        else{
          pushChar(b);
          int foo=getASCII(term_width-(x/char_width));
          if(foo!=0){
            //System.out.println("foo="+foo+" "+x+", "+(y-char_height)+" "+(x+foo*char_width)+" "+y+" "+buf+" "+bufs+" "+b+" "+buf[bufs-foo]);
            //System.out.println("foo="+foo+" ["+new String(buf, bufs-foo, foo));
            this.clear_area(x, y-char_height, x+foo*char_width, y);
            this.drawBytes(buf, bufs-foo, foo, x, y);
          }
          else{
            foo=1;
            this.clear_area(x, y-char_height, x+foo*char_width, y);
            b1[0]=getChar();
            this.drawBytes(b1, 0, foo, x, y);
            //System.out.print("["+Integer.toHexString(bar[0]&0xff)+"]");
          }
          x+=(char_width*foo);
          w=char_width*foo;
          h=char_height;
        }
        this.redraw(rx, ry-char_height, w, h);
        this.setCursor(x, y);
        this.draw_cursor();
      }
    
    public void paintComponent(Graphics g){
        super.paintComponent(g);
        if(img!=null){
            g.drawImage(img, 0, 0, term_area);
        }
    }

    public void paint(Graphics g){
        super.paint(g);
    }
    
    public void setSize(int w, int h){

        super.setSize(w, h);
        BufferedImage imgOrg=img;
        if(graphics!=null)graphics.dispose();

        int column=w/getCharWidth();
        int row=h/getCharHeight();
        term_width=column;
        term_height=row;
        
        reset();

        img=new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        graphics=(Graphics2D)(img.getGraphics());
        graphics.setFont(font);
        clear_area(0, 0, w, h);

        if(imgOrg!=null){
            Shape clip=graphics.getClip();
            graphics.setClip(0, 0, getTermWidth(), getTermHeight());
            graphics.drawImage(imgOrg, 0, 0, term_area);
            graphics.setClip(clip);
        }

        if(cursor_graphics!=null)cursor_graphics.dispose();
        cursor_graphics=(Graphics2D)(img.getGraphics());
        cursor_graphics.setColor(getForeGround());
        cursor_graphics.setXORMode(getBackGround());
        setAntiAliasing(antialiasing);

        if(imgOrg!=null){
            imgOrg.flush();
            imgOrg=null;
        }
    }
    
    public void setAntiAliasing(boolean foo){
        if(graphics==null)return;
        antialiasing=foo;
        java.lang.Object mode=foo ? RenderingHints.VALUE_TEXT_ANTIALIAS_ON : RenderingHints.VALUE_TEXT_ANTIALIAS_OFF;
        RenderingHints hints=new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING, mode);
        graphics.setRenderingHints(hints);
    }
    
}
